package primitive;
import java.util.*;
public class Arena {
	/**
	 * Grids specifications:
	 *
	 *  '_' empty
	 *  'A/B/C/D' players
	 *  'a/b/c/d' dead players
	 *  '#' wall
	 *  'G' grenade
	 *  'R' radar
	 *  '*' inactive mine
	 *  '1/2/3/4' countdown mine
	 *  'O' out of bound
	 */
	
	//Command Constants
	public static final int UP = 8;
	public static final int DOWN = 2;
	public static final int LEFT = 4;
	public static final int RIGHT = 6;
	
	//Arena Preset Constants
	private static final int ARENA_SIZE = 15;
	private static final int N_WALL = 50;
	/*
	private static final int N_RADAR = 0;
	private static final int N_MINE = 0;
	private static final int N_GRENADE = 0;
	*/
	
	//Game Setting Constants
	private static final int RANGE = 3;
	private static final int N_COMMANDS = 1;
	private static final int N_PLAYER = 4;

	//Game State Info 
	private static Grid[][] arena = new Grid[ARENA_SIZE][ARENA_SIZE];
	private static Bot[] player = new Bot[N_PLAYER];
	private static int active;
	private static int nAlive;
	private static String message="Game initialized";

	public static void main(String[] args) throws Throwable {
		//Initialization
		Arena arena = new Arena();
		arena.init();
		
		//Initial Display
		@SuppressWarnings("resource")
		Scanner sc = new Scanner(System.in);
		System.out.println("GridBot");
		System.out.println("-------------------------");
		display();
		sc.nextLine();

		//Process Actions
		while (nAlive > 1) {
			// each round
			int counter = 0;
			while (counter < N_PLAYER) {
				if (player[active].alive){
					player[active].updateVisible();
					player[active].resetMoveCount();
					player[active].run();
				}
				active = (active + 1) % 4;
				counter++;

			}
			//Control Flow Display
			display();
			sc.nextLine();
		}
	}

	private void init(){
		for (int i = 0 ; i < ARENA_SIZE; i ++){
			for (int j = 0 ; j < ARENA_SIZE; j ++){
				arena[i][j] = new Empty(i,j);
			}
		}
		//spawning players
		player[0] = new ZombieBot(this);
		player[1] = new ZombieBot(this);
		player[2] = new ZombieBot(this);
		player[3] = new ZombieBot(this);
		spawnPlayer(player);
		active = (int)(Math.random()*N_PLAYER);
		nAlive = N_PLAYER;
		
		//spawning environment
		/*
		spawnGrenade();
		spawnRadar();
		spawnMine();
		*/
		spawnWall();
	}
	private static void spawnPlayer(Bot[] players){
		char identifier = 'A';
		for (Bot player: players){
			boolean occupied = true;
			while (occupied){
				int i = (int) (Math.random()* ARENA_SIZE);
				int j = (int) (Math.random()* ARENA_SIZE);
				if (arena[i][j] instanceof Empty){
					occupied = false;
					arena[i][j] = player;
					player.setLocation(i, j);
					player.setIdentifier(identifier);
					identifier ++;
				}
			}
		}
	}
	/*
	private void spawnGrenade() {
		for (int n = 0; n < N_GRENADE; n ++ ){
			boolean occupied = true;
			int trial = 0;
			while (occupied && trial < ARENA_SIZE){
				int i = (int) (Math.random()* ARENA_SIZE);
				int j = (int) (Math.random()* ARENA_SIZE);
				if (arena[i][j] instanceof Empty){
					occupied = false;
					arena[i][j] = new Grenade(i,j);
				}
				trial ++;
			}
		}
	}
	private void spawnMine() {
		for (int n = 0; n < N_MINE; n ++ ){
			boolean occupied = true;
			int trial = 0;
			while (occupied && trial < ARENA_SIZE){
				int i = (int) (Math.random()* ARENA_SIZE);
				int j = (int) (Math.random()* ARENA_SIZE);
				if (arena[i][j] instanceof Empty){
					occupied = false;
					arena[i][j] = new Mine(i,j);
				}
				trial ++;
			}
		}
	}
	private void spawnRadar() {
		for (int n = 0; n < N_RADAR; n ++ ){
			boolean occupied = true;
			int trial = 0;
			while (occupied && trial < ARENA_SIZE){
				int i = (int) (Math.random()* ARENA_SIZE);
				int j = (int) (Math.random()* ARENA_SIZE);
				if (arena[i][j] instanceof Empty){
					occupied = false;
					arena[i][j] = new Radar(i,j);
				}
				trial ++;
			}
		}
	}
	*/
	private void spawnWall() {
		for (int n = 0; n < N_WALL; n ++ ){
			boolean occupied = true;
			int trial = 0;
			while (occupied && trial < ARENA_SIZE){
				int i = (int) (Math.random()* ARENA_SIZE);
				int j = (int) (Math.random()* ARENA_SIZE);
				if (arena[i][j] instanceof Empty){
					occupied = false;
					arena[i][j] = new Wall(i,j);
				}
				trial ++;
			}
		}
	}
	
	private static void display(){
		for(int i=0;i<20;i++){
			System.out.println();
		}
		System.out.println(message);
		for (int i = 0 ; i < ARENA_SIZE; i ++){
			for (int j = 0 ; j < ARENA_SIZE; j ++){
				System.out.print(arena[i][j]+" ");
			}
			System.out.println();
		}
	}
	private abstract class Grid{
		private int x;
		private int y;
		private Grid(){
			
		}
		private Grid(int y, int x){
			this.x = x;
			this.y = y;
		}
		protected abstract void isShot();
		protected abstract boolean isRunOver();
	}
	/*
	private class Mine extends Grid{
		public Mine(int y, int x) {
			super(y, x);
		}

		public String toString(){
			return "*";
		}

		@Override
		void isShot() {
			// TODO Auto-generated method stub
			
		}

		@Override
		boolean isRunOver() {
			// TODO Auto-generated method stub
			return false;
		}
	}
	private class Grenade extends Grid{
		public Grenade(int y, int x) {
			super(y, x);
		}

		public String toString(){
			return "*";
		}

		@Override
		void isShot() {
			// TODO Auto-generated method stub
			
		}

		@Override
		boolean isRunOver() {
			// TODO Auto-generated method stub
			return false;
		}
	}
	private class Radar extends Grid{
		public Radar(int y, int x) {
			super(y, x);
		}

		public String toString(){
			return "R";
		}

		@Override
		void isShot() {
			// TODO Auto-generated method stub
			
		}

		@Override
		boolean isRunOver() {
			// TODO Auto-generated method stub
			return false;
		}
	}
	*/
	private class Wall extends Grid{
		private Wall(int y, int x) {
			super(y, x);
		}

		public String toString(){
			return "#";
		}

		@Override
		protected void isShot() {
			arena[super.y][super.x] = new Empty(super.y,super.x);
		}

		@Override
		protected boolean isRunOver() {
			return false;
		}
	}
	private class Empty extends Grid{
		private Empty(int y, int x) {
			super(y, x);
		}

		public String toString(){
			return "_";
		}

		@Override
		protected void isShot() {
		}
		@Override
		protected boolean isRunOver() {
			return true;
		}
	}
	protected abstract class Bot extends Grid {
		private char identifier;
		private char[][] visible;
		private boolean alive;
		private int moveCount;
		private boolean radarEnabled;
		private int nGrenade;

		public Bot() {
			alive = true;
		}
		public abstract void run();
		protected void isShot(){
			if (alive){
				message = message +"\n"+identifier+" was shot by "+player[active].identifier+".";
				alive = false;
				identifier = (char) (identifier + ('a' - 'A'));
			}
		}
		protected boolean isRunOver(){
			return false;
		}
		private void setLocation(int y, int x){
			super.x = x;
			super.y = y;
		}
		private final void updateVisible(){
			char[][] copy = new char[2*RANGE+1][2*RANGE+1];
			for (int i = 0; i < 2*RANGE + 1; i ++){
				for (int j = 0; j < 2*RANGE + 1; j++){
					try {
						copy[i][j] = arena[super.y -RANGE + i][super.x -RANGE + j].toString().charAt(0);
					}
					catch(ArrayIndexOutOfBoundsException e){
						copy[i][j] = 'O';
					}
				}
			}	
		}
		private final void setIdentifier(char identifier){
			this.identifier = identifier;
		}
		private final void resetMoveCount(){
			moveCount = 0;
		}
		public final void shoot(int type){
			if (moveCount < N_COMMANDS){
				moveCount ++;
				int i = super.y;
				int j = super.x;
				switch(type){
				case UP:
					message += "\n"+ this.identifier+" fired up.";
					i--;
					break;
				case DOWN:
					i++;
					message += "\n"+ this.identifier+" fired down.";
					break;
				case LEFT:
					message += "\n"+ this.identifier+" fired left.";
					j--;
					break;
				case RIGHT:
					message += "\n"+ this.identifier+" fired right.";
					j++;
					break;
				default:
					i = -1;
					j = -1;
				}
				try{
					Grid current = arena[i][j];
					current.isShot();
				}
				catch(ArrayIndexOutOfBoundsException e){
				}
			}
		}
		public final void move(int type){
			if (moveCount < N_COMMANDS){
				moveCount ++;
				int i = super.y;
				int j = super.x;
				switch(type){
				case UP:
					i--;
					break;
				case DOWN:
					i++;
					break;
				case LEFT:
					j--;
					break;
				case RIGHT:
					j++;
					break;
				}
				try{
					Grid current = arena[i][j];
					if (current.isRunOver()){
						arena[super.y][super.x] = new Empty(super.y,super.x); 
						super.y = i;
						super.x = j;
						arena[i][j] = this;
					}
				}
				catch(ArrayIndexOutOfBoundsException e){
				}
			}
		}
		public int getX(){
			return super.x;
		}
		public int getY(){
			return super.y;
		}
		public char[][] getVisible(){
			return visible;
		}
		public int getNAlive(){
			return nAlive;
		}
		public int getNGrenade(){
			return nGrenade;
		}
		public boolean hasRadar(){
			return radarEnabled;
		}
		public String toString(){
			return ""+identifier;
		}
	}
	
}

